package com.zjsru.oneDay202203;

/**
 * @Author: likew
 * @Date: 2022/3/13
 * <p>
 * UTF-8 编码验证 对于 1 字节的字符，字节的第一位设为 0 ，后面 7 位为这个符号的 unicode 码。 对于 n 字节的字符 (n > 1)，第一个字节的前 n 位都设为1，第 n+1 位设为 0
 * ，后面字节的前两位一律设为 10 。剩下的没有提及的二进制位，全部为这个符号的 unicode 码。
 * <p>
 * 输入：data = [197,130,1] 输出：true 解释：数据表示字节序列:11000101 10000010 00000001。 这是有效的 utf-8 编码，为一个 2 字节字符，跟着一个 1 字节字符。
 * <p>
 * 输入：data = [235,140,4] 输出：false 解释：数据表示 8 位的序列: 11101011 10001100 00000100. 前 3 位都是 1 ，第 4 位为 0 表示它是一个 3 字节字符。
 * 下一个字节是开头为 10 的延续字节，这是正确的。 但第二个延续字节不以 10 开头，所以是不符合规则的。
 */
public class validUtf8 {
    /*
    * 从前往后处理每个 data[i]data[i]，先统计 data[i]data[i] 从第 7 位开始往后有多少位连续的 1，代表这是一个几字节的字符，记为 cntcnt ：
      如果 cntcnt 为 1 或者大于 44 均违反编码规则（与字符长度为 1 时的编码规则 和 字符长度只能是 1 到 4 冲突），返回 False；
      如果位置 ii 后面不足 cnt - 1cnt−1 也返回 False；
      否则检查下标范围为 [i + 1, i + cnt - 1][i+1,i+cnt−1] 的数是否满足前两位为 10 的要求，若不满足返回 False。
    * */
    public boolean validUtf8(int[] data) {
        int n = data.length;
        for (int i = 0; i < n; ) {
            int t = data[i], j = 7;
            while (j >= 0 && (((t >> j) & 1) == 1)) j--;
            int cnt = 7 - j;
            // 如果 cntcnt 为 1 或者大于 4 均违反编码规则（与字符长度为 11 时的编码规则 和 字符长度只能是 1 到 4 冲突），返回 False；
            if (cnt == 1 || cnt > 4) return false;
            //如果位置 ii 后面不足 cnt - 1cnt−1 也返回 False；
            if (i + cnt - 1 >= n) return false;
            for (int k = i + 1; k < i + cnt; k++) {
                //检查下标范围为 [i + 1, i + cnt - 1][i+1,i+cnt−1] 的数是否满足前两位为 1010 的要求，若不满足返回 False。
                if ((((data[k] >> 7) & 1) == 1) && (((data[k] >> 6) & 1) == 0)) continue;
                return false;
            }
            if (cnt == 0) {
                i++;
            } else {
                i += cnt;
            }
        }
        return true;
    }
    
    public static void main(String[] args) {
        validUtf8 validUtf8 = new validUtf8();
        int[] data = new int[] {197, 130, 1};
        System.out.println(validUtf8.validUtf8(data));
    }
}
